using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Aspose.ThreeD;
using Microsoft.Extensions.Configuration;
using System.IO;
using System.Runtime.InteropServices;

namespace Aspose.App.Api.Services
{
    public class StorageService
    {
        private string workingDirectory;

        public StorageService(IConfiguration configuration)
        {
            workingDirectory = configuration["SystemConfig:WorkingDirectory"];
        }
        /// <summary>
        /// Gets the repository for storing files of each app
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public StorageRepository GetRepository(string name)
        {
            if (string.IsNullOrEmpty(name))
                return null;
            return new StorageRepository(workingDirectory, name);
        }
    }
    public class StorageRepository
    { 
        private string baseDirectory;
        public StorageRepository(string workingDirectory, string name)
        {
            baseDirectory = Path.Combine(workingDirectory, name);
            if (!Directory.Exists(baseDirectory))
                Directory.CreateDirectory(baseDirectory);
        }


        /// <summary>
        /// Generate a file name instance, and create its directory on disk
        /// </summary>
        /// <returns></returns>
        public StorageFileNames NewNames()
        {
            //Generate a special guid, first two bytes are for date encryption.
            var bytes = new byte[16];
            var r = new Random();
            r.NextBytes(bytes);
            var now = DateTime.Now;
            var date = ((now.Year - 2020) * 13 + now.Month) * 32 + now.Day;
            bytes[2] = (byte)(bytes[0] ^ ((date >> 8) & 0xff));
            bytes[3] = (byte)(bytes[1] ^ (date & 0xff));
            //last byte is the checksum
            int checksum = 0;
            for (int i = 0; i < 15; i++)
                checksum += bytes[i];
            bytes[15] = (byte)checksum;
            var id = new Guid(bytes);
            //Restore the file name from the guid
            var ret = ParseNames(id);
            if(!Directory.Exists(ret.Directory))
                Directory.CreateDirectory(ret.Directory);
            return ret;
        }
        public StorageFileNames ParseNames(string sessionId)
        {
            if (string.IsNullOrEmpty(sessionId))
                return null;
            Guid result;
            if (!Guid.TryParse(sessionId, out result))
                return null;
            return ParseNames(result);
        }
        public StorageFileNames ParseNames(Guid sessionId)
        {
            var bytes = sessionId.ToByteArray();
            var checksum = 0;
            for (int i = 0; i < 15; i++)
                checksum += bytes[i];
            //Invalid checksum, means it's not generated by StorageService
            if (bytes[15] == (byte)checksum)
                return ParseNamesV1(sessionId, bytes);
            return null;
        }
        private StorageFileNames ParseNamesV1(Guid sessionId, byte[] bytes)
        { 
            
            //Restore the date from the Guid
            var high = (byte)(bytes[0] ^ bytes[2]);
            var low = (byte)(bytes[1] ^ bytes[3]);
            var date = (high << 8) | low;
            var day = date % 32;
            var month = (date - day) / 32 % 13;
            var year = date / (13 * 32);
            var dateString = string.Format("{0:0000}-{1:00}-{2:00}", year + 2020, month, day);

            var id = sessionId.ToString();
            //Generate the directory and initialize the file names
            var baseDir = Path.Combine(baseDirectory, dateString, id);
            var ret = new StorageFileNames(id, baseDir);
            return ret;
        }


    }

    public sealed class FileNameEntry
    {
        private string entry;
        public FileNameEntry(string entry)
        {
            this.entry = entry;
        }
        public override string ToString()
        {
            return entry;
        }
    }
    public sealed class StorageFileNames
    {
        /// <summary>
        /// The session id that passed to client side
        /// </summary>
        public string Id { get; }
        /// <summary>
        /// The directory to store files
        /// </summary>
        public string Directory { get; }

        public StorageFileNames(string id, string dir)
        {
            this.Id = id;
            this.Directory = dir;
        }
        public string this[FileNameEntry kind]
        {
            get
            {
                return GetFileName(kind, 0);
            }
        }
        public string GetFileName(FileNameEntry kind, int index)
        {
            var ret = Path.Combine(Directory, kind.ToString());
            if (index == 0)
                return ret;
            return ret + (index + 1);
        }
    }
}
